package com.lavender.aop.proxy.design;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * <p>
 * cglib动态代理：代码参考：https://www.jianshu.com/p/b25cba7e438
 * <p>
 * 注：需要引入cglib的包，本次直接引入aop的包。
 * 核心类：
 * MethodInterceptor：方法拦截器，作为Enhancer生成代理类的回调。
 * Enhancer：创建代理类。
 * </p>
 *
 * @author: zhu.chen
 * @date: 2020/8/3
 * @version: v1.0.0
 */
public class CglibDynamicProxy {

    public static void main(String[] args) {
        // 生成运行中的字节码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\software\\gitee\\lavender-spring-boot\\lavender-aop\\target");
        CglibMethodInterceptor interceptor = new CglibMethodInterceptor();
        UserService userService = (UserService) interceptor.getInstance(new UserService());
        userService.addUser();
    }

    public static class UserService {

        public void addUser() {
            System.out.println("增加一个用户。。。");
            this.addTest(); // 1 不管使用invokeSuper还是invoke，均不会被代理类调用。
            this.addTest1(); // 2 使用invokeSuper，依旧会被代理类调用。
            this.addTest2(); // 3 使用invoke，不会被代理类调用。spring就是这种写法，保留了ProxyBean和原生Bean。
        }

        /**
         * 1：
         * 类内方法调用private方法不会被代理类拦截：
         * 原因：
         * 因为cglib的子类是增强类，增强类没继承addTest私有方法，所以addUser调的是父类的方法addTest，而不是增强类，所以私有addTest没被拦截。
         */
        private void addTest() {
            System.out.println("this is a test.");
        }

        /**
         * 2：
         * 类内方法调用public方法会被代理类拦截。
         * 问题：原生CGLib内部方法互相调用时可以代理，但基于CGLib的Spring AOP却代理失效，为什么？
         * 原因：https://segmentfault.com/q/1010000009997165
         */
        public void addTest1() {
            System.out.println("this is a test1.");
        }

        /**
         * 3：
         * 类内方法调用public方法会被代理类拦截。
         * 问题：原生CGLib内部方法互相调用时可以代理，但基于CGLib的Spring AOP却代理失效，为什么？
         * 原因：https://segmentfault.com/q/1010000009997165
         */
        public void addTest2() {
            System.out.println("this is a test2.");
        }

    }

    public static class CglibMethodInterceptor implements MethodInterceptor {

        private Object target;

        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            // 设置父类
            enhancer.setSuperclass(this.target.getClass());
            // 设置回调方法 ---> 将当前的MethodInterceptor设置为回调
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始");
            /**
             * 调用的是代理类，通过代理类调用父类，因此本地调用也会被拦截。
             *
             * 3：
             * 类内方法调用public方法会被代理类拦截。
             * 问题：原生CGLib内部方法互相调用时可以代理，但基于CGLib的Spring AOP却代理失效，为什么？
             * 原因：https://segmentfault.com/q/1010000009997165
             *
             * public void addTest2 () {System.out.println("this is a test2.");}
             */
//            Object result = methodProxy.invokeSuper(o, objects);

            /**
             * spring的写法，调用的是原生类，因此会导致下列失效。
             *
             * 2：
             * 类内方法调用public方法会被代理类拦截。
             * 问题：原生CGLib内部方法互相调用时可以代理，但基于CGLib的Spring AOP却代理失效，为什么？
             * 原因：https://segmentfault.com/q/1010000009997165
             * spring aop在MethodInterceptor的实现类的intercept的方法中调用的不是proxy.invokeSuper(obj,args);此处obj是代理对象，
             * 而是proxy.invoke(target,args);此处target为原始对象，改成proxy.invoke(new Foo(),args);则调用本类方法是不会被代理拦截的。
             *
             * public void addTest1(){System.out.println("this is a test1.");}
             */
            Object result = methodProxy.invoke(new UserService(), objects); //
            System.out.println("结束");
            return result;
        }
    }

}
